#ifndef _ZEVENT_ZYS_SML_HPP__
#define _ZEVENT_ZYS_SML_HPP__

#include "queue.hpp"
#include "sml.hpp"
namespace zevent {
namespace sml {

using namespace boost::sml;

/// \brief Template alias for creating events of type T
/// \tparam T The event data type
template <typename T> auto zevt = boost::sml::event<event::internal::Event<T>>;

/// \brief State machine wrapper class providing high-level encapsulation for
/// boost::sml \tparam TConfig State machine configuration type \tparam
/// TPolicies State machine policy parameter pack
template <typename TConfig, typename... TPolicies> class Sm {
public:
  /// \brief Check if the current state machine is in a specific state
  /// \param s The state to check
  /// \return true if currently in the specified state, false otherwise
  static constexpr auto IsState(auto s) { return m_sm->is(s); }

  template <std::size_t kQueueSize = 0, typename... TArgs>
  static auto &Create(TArgs... args) {

    static SmlType sml_sm{args...};
    [[maybe_unused]] static Sm sm;
    m_sm = &sml_sm;

    if constexpr (kQueueSize > 0) {
      sm.InitQueue<kQueueSize>(typename SmlType::events{});
    } else {
      sm.SetDirectEventsCall(typename SmlType::events{});
    }
    return sm;
  }

  static auto ProcessEvents() {

    if (m_cb_process_events != nullptr) {
      m_cb_process_events();
    }
  }

private:
  /// \brief Disable copy constructor
  Sm(const Sm &) = delete;

  /// \brief Disable move constructor
  Sm(const Sm &&) = delete;

  /// \brief Disable move assignment operator
  auto operator()(const Sm &&) = delete;

  /// \brief Disable copy assignment operator
  auto operator()(const Sm &) = delete;

  /// \brief Constructor that connects event handling
  constexpr Sm() = default;

  using SmlType = boost::sml::sm<TConfig, TPolicies...>;

  /// \brief Connect events to their corresponding service handlers
  /// \tparam TList Event list template type
  /// \tparam TEvents Event type parameter pack
  /// \param Event list object
  template <template <typename...> typename TList, typename... TEvents>
  auto SetDirectEventsCall(TList<TEvents...>) {
    (TEvents::ServiceType::Connect([](auto... args) {
       m_sm->process_event(TEvents{std::make_tuple(args...)});
     }),
     ...);
  }
  template <std::size_t kSize, template <typename...> typename TList,
            typename... TEvents>
  auto InitQueue(TList<TEvents...>) {
    using QueueType = Queue<kSize, typename TEvents::ServiceType...>;
    [[maybe_unused]] static QueueType q;
    (QueueType::template Connect<typename TEvents::ServiceType>(
         [](auto... args) {
           m_sm->process_event(TEvents{std::make_tuple(args...)});
         }),
     ...);
    m_cb_process_events = &QueueType::ProcessEvents;
  }

  inline static SmlType *m_sm;
  inline static void (*m_cb_process_events)();
};
} // namespace sml
} // namespace zevent

#endif // !_ZEVENT_ZYS_SML_HPP__